def total_seconds(x):
    return float((x.microseconds+(x.seconds+x.days*24*3600)*10**6)/10**6)

class TimeUnit:
    def __init__(self):
        # Type of Units
        # - 1.year
        # - 2.month
        # - 3.week
        # - 4.day
        # - 5.hour
        # - 6.minute
        # - 7.second
        import re
        self.filter_str=['year','month','week','day','hour','minute','second']
        self.date_filter=[]
        self.date_filter.append(re.compile(r"(\d+)\s*y"))
        self.date_filter.append(re.compile(r"(\d+)\s*mo"))
        self.date_filter.append(re.compile(r"(\d+)\s*w"))
        self.date_filter.append(re.compile(r"(\d+)\s*d"))
        self.date_filter.append(re.compile(r"(\d+)\s*h"))
        self.date_filter.append(re.compile(r"(\d+)\s*mi"))
        self.date_filter.append(re.compile(r"(\d+)\s*s")) 
    
    def DataFilterGenerator(self):
        for f in self.date_filter:
            yield f
    
    def FilterString(self,i):
        return self.filter_str[i]

    def GetTimeDelta(self,i,x):
        import datetime
        if i == 0:
            return datetime.timedelta(days=365.15*x)
        elif i == 1:
            return datetime.timedelta(days=30.5*x)
        elif i == 2:
            return datetime.timedelta(weeks=x)
        elif i == 3:
            return datetime.timedelta(days=x)
        elif i == 4:
            return datetime.timedelta(hours=x)
        elif i == 5:
            return datetime.timedelta(minutes=x)
        elif i == 6:
            return datetime.timedelta(seconds=x)
        else:
            raise Exception

    def TransToTimeDelta(self,st):
        import datetime
        delt=datetime.timedelta(days=0)
        for i,item in enumerate(self.DataFilterGenerator()):
            se=item.search(st)
            if bool(se):
                try:
                    delt+=self.GetTimeDelta(i,float(se.group(1)))
                except AttributeError:
                    print("Something wrong in time data..")
        return delt

class InputSpecifier:
    import re
    def __init__(self):
        import re
        ################################################################
        # key words in input file
        ################################################################
        self.ifilter1=[]
        # 0. NFLOW
        self.ifilter1.append(re.compile(r"\bNFLOW\b\s+(\d+)"))
        # 1. FLOW(\d+)
        self.ifilter1.append(re.compile(r"\bFLOW(\d+)"))
        # 2. LINK
        self.ifilter1.append(re.compile(r"\bLINK\b\s+(\d+)"))
        # 3. GENERIC_MODEL
        self.ifilter1.append(re.compile(r"\bGENERIC_MODEL\b"))
        # 4. GENERIC_GROUP
        self.ifilter1.append(re.compile(r"\bGENERIC_GROUP\b"))
        # 5. PATH_MODEL
        self.ifilter1.append(re.compile(r"\bPATH_MODEL\b"))
        # 6. PATH_GROUP
        self.ifilter1.append(re.compile(r"\bPATH_GROUP\b"))
        # 7. STAGE_PROPERTY
        self.ifilter1.append(re.compile(r"\bSTAGE_PROPERTY\b\s+(\d+)\s*[+]\s*(\d+)"))
        # 8. LINK_PROPERTY
        self.ifilter1.append(re.compile(r"\bLINK_PROPERTY\b"))
        # 9. RUN
        self.ifilter1.append(re.compile(r"\bRUN\b\s*(\d+)"))
        # 10. stage(\d+)
        self.ifilter1.append(re.compile(r"\bstage(\d+)"))
        # 11. MODEL
        self.ifilter1.append(re.compile(r"\bMODEL(\d+)\s+(\w+)"))
        # 12. GROUP
        self.ifilter1.append(re.compile(r"\bGROUP(\d+)"))
        # 13. LINK DEFINITION
        self.ifilter1.append(re.compile(r"\b(\d+)\s+[.]\s+(\d+)\s+[+]\s+(\d+)\s+[-]\s+[>]\s+(\d+)\s+[+]\s+(\d+)"))
        # 14. SEED
        self.ifilter1.append(re.compile(r"\bSEED\b\s+(\d+)"))
        # 15. EXE
        self.ifilter1.append(re.compile(r"\bEXE\b\s+(\d+)"))
        # 16. PRINT
        self.ifilter1.append(re.compile(r"\bPRINT\b"))


        ################################################################
        # key words for stage property
        ################################################################
        self.ifilter2=[]
        # 0. DEFAULT
        self.ifilter2.append(re.compile(r"\b(DEFAULT)\b"))
        # 1. LINK
        self.ifilter2.append(re.compile(r"\b(LINK)\b"))
        # 2. END
        self.ifilter2.append(re.compile(r"\b(END)\b"))

        ################################################################
        # group separator
        ################################################################
        self.ifilter3=re.compile(r"\s+")

        ################################################################
        # model number in group
        ################################################################
        self.ifilter4=re.compile(r"\bMODEL(\d+)")

    def if1gen(self):
        for item in self.ifilter1:
            yield item

    def if2gen(self):
        for item in self.ifilter2:
            yield item

class BasicModel():

    __count=0

    def __init__(self,st,n):
        BasicModel.__count+=1
        self.filename=st.strip()
        self.id=n
        self.modelname=[]
        self.data=[]

    def get_id(self):
        return self.id

    def set_modelname(self,st):
        self.modelname.append(st.strip())
        return self

    def init_data(self,n,default):
        self.data.append([default]*n)
        return self

    def set_data(self,n1,n2,x):
        self.data[n1][n2]=x
        return self

    def add_model(self,st,n,default=0.0):
        self.set_modelname(st)
        self.init_data(n,default)
        return self

    def get_filename(self):
        return self.filename
    
    def get_modelname(self,n):
        return self.modelname[n]

    def get_nmodel(self):
        return len(self.modelname)

    def get_ndata(self,n):
        return len(self.data[n])

    def get_data(self,n1,n2):
        return self.data[n1][n2]

    def str2index(self,st):
        s=st.strip()
        for i,item in enumerate(self.modelname):
            if item == s:
                return i
        return -1

    def sCount():
        return BasicModel.__count

    def cCount(cls):
        return cls.__count

    scount = staticmethod(sCount)
    ccount = classmethod(cCount)

# class GenericModel(BasicModel):

#     def __init__(self,st,n):
#         BasicModel.__init__(self,st,n)

class PathModel(BasicModel):

    def __init__(self,st,n):
        BasicModel.__init__(self,st,n)
        # Summation all fields for each data
        self.sumdata=[]

    def add_model(self,st,n,default=0.0):
        BasicModel.add_model(self,st,n,default)
        self.sumdata.append(0.0)
        return self

    def set_data(self,n1,n2,x):
        BasicModel.set_data(self,n1,n2,x)
        self.sumdata[n1]=self.sumdata[n1]+x
        return self

    def add_data(self,i,j,x):
        self.data[i][j]+=x
        self.sumdata[i]=self.sumdata[i]+x
        return self

    def set_sumdata(self,n,x):
        self.sumdata[n]=x
        return self

    def get_sum(self,n):
        return self.sumdata[n]

    def get_sum2(self):
        sum2=0.0
        for i in range(BasicModel.get_nmodel(self)):
            sum2=sum2+self.sumdata[i]
        return sum2

class BasicStageObject():

    __count = 0

    def __init__(self,n):
        self.__count+=1
        self.id=n
        self.next_flow=[]
        self.next_stage=[]
        self.path_prob=[]
        self.path_prop=[]
        self.path_info=[]
        self.stopcount=[]
        self.flowcount=[]

    def get_id(self):
        return self.id

    def set_next_flow(self,n):
        self.next_flow.append(n)
        return self

    def set_next_stage(self,n):
        self.next_stage.append(n)
        return self

    def get_next_flow(self,pn):
        return self.next_flow[pn]

    def get_next_stage(self,pn):
        return self.next_stage[pn]

    def set_path_prob(self,x):
        self.path_prob.append(x)
        return self

    def set_path_prop(self,x):
        self.path_prop.append(x)
        return self

    def set_path_info(self,d):
        self.path_info.append(d)
        return self

    def set_stopcount(self):
        self.stopcount.append(0)
        return self

    def set_flowcount(self):
        self.flowcount.append(0)
        return self

    def get_npath(self):
        return len(self.next_flow)

    def sCount():
        return BasicStageObject.__count

    def cCount(cls):
        return cls.__count

    scount = staticmethod(sCount)
    ccount = classmethod(cCount)

    def countstop(self,n):
        self.stopcount[n]+=1
        return self

    def countflow(self,n):
        self.flowcount[n]+=1
        return self

    def get_stopcount(self,n):
        return self.stopcount[n]

    def get_flowcount(self,n):
        return self.flowcount[n]

class StageObject(BasicStageObject):
    def __init__(self,n):
        BasicStageObject.__init__(self,n)
        # self.generic_model=[]
        self.path_model=[]

    # def get_ngmodel(self):
    #     return len(self.generic_model)

    # def get_ngmodel2(self,n):
    #     return len(self.generic_model[n])
        
    def set_next(self,sn1,sn2,p):
        self.set_next_flow(sn1)
        self.set_next_stage(sn2)
        self.set_path_prob(0.0)
        self.set_path_prop(p)
        self.set_stopcount()
        self.set_flowcount()
        # self.generic_model.append([])
        self.path_model.append([])
        st="To: flow={0}, stage={1}, prop={2}".format(sn1,sn2,p)
        self.set_path_info(PathModel(st,0))
        return self

    def set_model(self,pn,mid,flag):
        if flag == 1:
            # generic model
            # self.generic_model[pn].append(mid)
            pass
        elif flag == 2:
            # generic group
            # self.generic_model[pn].extend(mid)
            pass
        elif flag == 3:
            # path model
            self.path_model[pn].append(mid)
        elif flag ==4:
            # path group
            self.path_model[pn].extend(mid)
        return self

    def add_path_info(self,pn,d):
        t=self.path_info[pn]
        # Check if name of model is same
        for i,item1 in enumerate(d.modelname):
            flag=False
            for j,item2 in enumerate(t.modelname):
                if item1 == item2:
                    n=j
                    flag=True
                    break
            if flag:
                t.modelname[n]=d.get_modelname(i)
                t.data[n]=[]
                t.sumdata[n]=0
                for j in range(d.get_ndata(i)):
                    val=d.get_data(i,j)
                    t.data[n].append(val)
                    t.sumdata[n]=t.sumdata[n]+val
            else:
                # Create new element
                t.add_model(d.get_modelname(i),\
                            d.get_ndata(i),\
                            0.0)
                n=t.get_nmodel()-1
                for j in range(d.get_ndata(i)):
                    t.set_data(n,j,d.get_data(i,j))

class BasicFlowObject():

    __count = 0

    def __init__(self,n):
        self.__count+=1
        self.id=n
        self.stage=[]

    def id2index(self,idn):
        for i,item in enumerate(self.stage):
            if idn == item.get_id():
                return i
        return -1

    def get_nstage(self):
        return len(self.stage)

    def set_stage(self,s):
        self.stage.append(s)
        return self

    def get_id(self):
        return self.id

    def sCount():
        return BasicFlowObject.__count

    def cCount(cls):
        return cls.__count

    scount = staticmethod(sCount)
    ccount = classmethod(cCount)

class FlowObject(BasicFlowObject):
    def __init__(self,n):
        BasicFlowObject.__init__(self,n)

class InputParam:
    def __init__(self):
        self.nrun=0
        self.nexe=0
        # SEED (Default)
        self.seed=-1
        # flow and stage
        self.flow=[]
        # GENERIC_MODEL
        # self.generic_model=[]
        # self.generic_group=[]
        # PATH_MODEL
        self.path_model=[]
        self.path_group=[]
        # OUTPUT
        self.outnode=[]

    # Create flow
    def set_flow(self,fid):
        self.flow.append(FlowObject(fid))
        return self

    def get_nflow(self):
        return len(self.flow)

    def id2findex(self,fid):
        for i,item in enumerate(self.flow):
            if fid == item.get_id():
                return i
        return -1

    # Create stage
    def set_stage_in_flow(self,fn,sid):
        self.flow[fn].set_stage(StageObject(sid))

    def get_nstage(self,fn):
        return self.flow[fn].get_nstage()

    def id2sindex(self,fn,sid):
        sn=self.flow[fn].id2index(sid)
        if sn < 0:
            return -1
        return sn

    # Access to flow and stage
    def id2index(self,fid,sid):
        fn=self.id2findex(fid)
        sn=self.id2sindex(fn,sid)
        return fn,sn

    def afs1(self,fn,sn):
        return self.flow[fn].stage[sn]

    def afs2(self,fid,sid):
        fn,sn=self.id2index(fid,sid)
        return self.afs1(fn,sn)

    def af1(self,fn):
        return self.flow[fn]

    def af2(self,fid):
        return self.flow[self.id2findex(fid)]

    # Generic model list
    # def set_generic_model(self,st,gid):
    #     self.generic_model.append(GenericModel(st,gid))
    #     return self

    # def id2gmodel(self,gid):
    #     for i,item in enumerate(self.generic_model):
    #         if gid == item.get_id():
    #             return i
    #     return -1

    # def gmodel1(self,gn):
    #     return self.generic_model[gn]

    # def gmodel2(self,gid):
    #     gn=self.id2gmodel(gid)
    #     if gn < 0:
    #         raise Exception
    #     else:
    #         return self.generic_model[gn]

    # def get_ngmodel(self):
    #     return len(self.generic_model)

    # Generic group list (input-index)
    # def set_generic_group(self,ls):
    #     self.generic_group.append(ls[:])

    # def get_nggroup(self):
    #     return len(self.generic_group)

    # def get_n_in_ggroup(self,gn):
    #     return len(self.generic_group[gn])

    # def getggarr(self,gn):
    #     # Return GENERIC group array
    #     return self.generic_group[gn]

    # Path model list
    def set_path_model(self,st,pid):
        self.path_model.append(PathModel(st,pid))
        return self

    def id2pmodel(self,pid):
        for i,item in enumerate(self.path_model):
            if pid == item.get_id():
                return i
        return -1

    def get_npmodel(self):
        return len(self.path_model)

    def pmodel1(self,pn):
        return self.path_model[pn]

    def pmodel2(self,pid):
        pn=self.id2pmodel(pid)
        if pn < 0:
            raise Exception
        else:
            return path_model[pn]

    # Path group list (input-index)
    def set_path_group(self,ls):
        self.path_group.append(ls[:])

    def get_npgroup(self):
        return len(self.path_group)

    def get_n_in_pgroup(self,pn):
        return len(self.path_group[pn])

    def getpgarr(self,pn):
        # Return PATH group array
        return self.path_group[pn]

    # Set nout
    def set_nout(self,fn,sn,mn):
        self.outnode.append(OutputObject(fn,sn,mn))
        return self

    def get_nout(self):
        return len(self.outnode)

    def get_out(self,on):
        return self.outnode[on].flow,self.outnode[on].stage,self.outnode[on].link

    def set_prob(self,fn,sn,pn):
        self.afs1(fn,sn).path_prob[pn]=1.0/float(self.afs1(fn,sn).path_info[pn].get_sum2())
        return self

    def get_prob(self,fn,sn,pn):
        return self.afs1(fn,sn).path_prob[pn]

    def get_prop(self,fn,sn,pn):
        return self.afs1(fn,sn).path_prop[pn]

    def zerocount(self):
        for i in range(self.get_nflow()):
            for j in range(self.flow[i].get_nstage()):
                for k in range(self.flow[i].stage[j].get_npath()):
                    self.flow[i].stage[j].stopcount[k]=0
                    self.flow[i].stage[j].flowcount[k]=0

class OutputObject:
    def __init__(self,i,j,k):
        self.flow=i
        self.stage=j
        self.link=k

iparam=InputParam()
timeunit=TimeUnit()

